<template>
  <canvas class="c-canvas" type="2d" ref="canvasRef" :style="`width:${props.maxWidth}rpx;height:${maxHeight}rpx`" id="canvas"></canvas>
</template>
<script setup lang="jsx">
import { builderImgUrl } from '@/hooks';
import { Canvas } from '@tarojs/components';
import { createSelectorQuery, useReady, canvasToTempFilePath } from '@tarojs/taro';
import { reactive, ref, shallowRef } from 'vue';

const canvasRef = shallowRef();
const props = defineProps({
  maxWidth: {
    type: Number,
    default: 750
  }
});
const maxHeight = ref(750);

let canvas = null;
let ctx = null;
const state = reactive({
  width: 750,
  height: 750
});
let scale = 2;
const drawAttrs = async attrs => {
  if (!attrs) return;
  for (let i = 0; i < attrs.length; i++) {
    const element = attrs[i];
    if (!element.value) continue;
    if (element.type == 'text') {
      drawText(element.value, element.x, element.y, element.width, element.height, element.attrs, scale);
    }
    if (element.type == 'qrcode' || element.type == 'img') {
      if (element.attrs.radius) {
        clipRoundRect(element.x, element.y, element.width, element.height, Number(element.attrs.radius), 'transparent', scale);
        ctx.clip();
      }
      await drawImage(element.value, element.x, element.y, element.width, element.height, scale);
      if (element.attrs.radius) {
        ctx.restore();
      }
    }
  }
};

// 写字
const drawText = (text, x, y, w, h, attrs, c = 1) => {
  if (c > 0 && c != 1) {
    x *= c;
    y *= c;
    w *= c;
    h *= c;
    attrs.size *= c;
    if (attrs.align == 'right') {
      x += w;
    } else if (attrs.align == 'center') {
      x += w / 2;
    }
  }
  let lineHeight = attrs.lineHeight ?? 1.6;
  let rows = attrs.rows ?? 1;

  const doText = (str, x, y) => {
    ctx.fillText(str, x, y);
  };
  ctx.fillStyle = attrs.color ?? '#000';
  ctx.font = attrs.size + 'px "Microsoft YaHei",sans-serif';
  ctx.textBaseline = 'top';
  ctx.textAlign = attrs.align;
  measureText(text, attrs.size, w, (str, line, hasNext) => {
    if (rows) {
      if (line < rows) {
        if (line + 1 == rows && hasNext) {
          doText(str.substr(0, str.length - 1) + '…', x, y + line * lineHeight);
        } else {
          doText(str, x, y + line * lineHeight);
        }
      }
    } else {
      doText(str, x, y + line * lineHeight);
    }
  });
};

const measureText = (text, fontSize, maxWidth, cb) => {
  function getWidth(item) {
    if (/[a-zA-Z]/.test(item)) {
      return 7;
    } else if (/[0-9]/.test(item)) {
      return 5.5;
    } else if (/\./.test(item)) {
      return 2.7;
    } else if (/-/.test(item)) {
      return 3.25;
    } else if (/\(|\)/.test(item)) {
      return 3.73;
    } else if (/\s/.test(item)) {
      return 2.5;
    } else if (/%/.test(item)) {
      return 8;
    } else {
      return 10;
    }
  }
  var texts = String(text).split('');
  var width = 0;
  var str = '';
  var line = 0;
  texts.forEach(function (item, i) {
    str += item;
    width += getWidth(item);
    if (i + 1 == texts.length) {
      return cb(str, line, false);
    }
    if (texts[i + 1] == '\n' || ((width + getWidth(texts[i + 1])) / 10) * fontSize > maxWidth) {
      cb(str, line++, true);
      str = '';
      width = 0;
    }
  });
};

const clipRoundRect = (x, y, w, h, r, color = '#000000', c = 1) => {
  if (c > 0 && c != 1) {
    x *= c;
    y *= c;
    w *= c;
    h *= c;
  }
  ctx.save();
  ctx.beginPath();
  if (w < 2 * r) {
    r = w / 2;
  }
  if (h < 2 * r) {
    r = h / 2;
  }
  ctx.moveTo(x + r, y);
  ctx.arcTo(x + w, y, x + w, y + h, r);
  ctx.arcTo(x + w, y + h, x, y + h, r);
  ctx.arcTo(x, y + h, x, y, r);
  ctx.arcTo(x, y, x + w, y, r);
  ctx.closePath();
  ctx.strokeStyle = color;
  ctx.stroke();
};

const drawImage = (src, x, y, w, h, c = 1) => {
  if (c > 0 && c != 1) {
    x *= c;
    y *= c;
    w *= c;
    h *= c;
  }
  return new Promise((resolve, rejcet) => {
    const image = canvas.createImage();
    image.onload = () => {
      ctx.drawImage(image, x, y, w, h);
      resolve();
    };
    image.src = builderImgUrl(src);
  });
};

const initCanvas = () => {
  canvas.width = state.width;
  canvas.height = state.height;
  ctx.scale(1, 1);
};

const loadContext = () => {
  return new Promise(resolve => {
    createSelectorQuery()
      .select('#canvas')
      .fields({ node: true, size: true })
      .exec(res => {
        canvas = res[0].node;
        ctx = canvas.getContext('2d');
        resolve(canvas);
      });
  });
};

useReady(() => {
  loadContext();
});

defineExpose({
  saveImage() {
    return new Promise((resolve, reject) => {
      canvasToTempFilePath({
        canvas,
        success: r => {
          resolve(r.tempFilePath);
        },
        fail(e) {
          reject(e.errMsg);
        }
      });
    });
  },
  async load(backgroundImg, width, height, attrs) {
    if (!backgroundImg) throw new Error('拉起分享失败');
    // 画背景
    state.width = 750;
    state.height = 750 / (width / height);
    maxHeight.value = props.maxWidth * (height / width);
    initCanvas();
    await drawImage(backgroundImg, 0, 0, state.width, state.height);
    await drawAttrs(attrs);
  }
});
</script>
<style lang="scss">
.c-canvas {
  transform-origin: center;
}
</style>
